﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pacman.Simulator;
using Pacman.Simulator.Ghosts;
using System.Diagnostics;

namespace PacmanAI
{
    public enum ObstacleType { Wall, Ghost, Blocked }; 
    // order is significant -- its worse to hit a ghost than a wall

    public class PathToObstacle 
    {
        private bool _hasPowerPill = false;
        private bool _hasPill = false;

        public bool HasPowerPill
        {
            get { return _hasPowerPill; }
            set { _hasPowerPill = value; }
        }

        public bool HasPill
        {
            get { return _hasPill; }
            set { _hasPill = value; }
        }

        private ObstacleType _obstacle = ObstacleType.Blocked;

        public ObstacleType Obstacle
        {
            get { return _obstacle; }
            set { _obstacle = value; }
        }
        private Direction _direction = Direction.Stall;

        public Direction Direction
        {
            get { return _direction; }
            set { _direction = value; }
        }
        private int _length = 0;

        public int Length
        {
            get { return _length; }
            set { _length = value; }
        }

        public override string ToString()
        {
            return string.Format("{0}:{2} to {1} in {3}", base.ToString(), 
                Obstacle, Direction, Length);
        }

        public static Comparison<PathToObstacle> CompareLengths =
            delegate(PathToObstacle a, PathToObstacle b) { return a.Length.CompareTo(b.Length); };

        /// <summary>
        /// Higher value is safer
        /// </summary>
        private int EscapeSafety // 6779
        {
            get
            {
                int offset = 0;                
                if (Obstacle == ObstacleType.Ghost)
                    offset -= 5;
                if (HasPowerPill)
                    offset += 10;
                return offset + Length;
            }
        }
        /// <summary>
        /// Less than 0 means a is better
        /// More than 0 means b is better
        /// </summary>
        public static Comparison<PathToObstacle> CompareEscapeSafety =
            delegate(PathToObstacle a, PathToObstacle b) 
            {
                return a.EscapeSafety.CompareTo(b.EscapeSafety); 
            }; 

    }

    static public class PathQueries
    {
        static bool IsChasingGhostAt(Ghost[] gs, Node n)
        {
            foreach (var g in gs)
            {
                if (g.Chasing &&
                    (g.Node.X == n.X && g.Node.Y == n.Y))                
                    return true;
            }
            return false;

        }
        static PathToObstacle Path(Ghost[] gs, Node from, Direction dir)
        {
            PathToObstacle result = new PathToObstacle() 
                { 
                    Direction = dir 
                };
            Node at = from.Beside(dir);
            while (true)
            {
                if (at.Type == Node.NodeType.Wall)
                {
                    result.Obstacle = ObstacleType.Wall;
                    return result;
                }
                if (at.Type == Node.NodeType.PowerPill)
                {
                    result.HasPowerPill = true;
                }
                if (at.Type == Node.NodeType.Pill)
                    result.HasPill = true; 
                if (IsChasingGhostAt(gs, at))
                {
                    result.Obstacle = ObstacleType.Ghost;
                    return result;
                }
                result.Length++;
                at = at.Beside(dir);
                Debug.Assert(result.Length < 100, "Loop detected -- we expect to hit a wall");
            }
        }

        public static PathToObstacle BestPath(this List<Direction> dirs, Node from, Ghost[] gs, Comparison<PathToObstacle> comp)
        {
            var paths = dirs.Map(
                delegate (Direction d) { return Path(gs,from,d); }
            );
            PathToObstacle r = paths.FindBest(comp);
            return r;
        }
    }
}
